/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.uml_generator.manager.impl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.genia.toolbox.basics.bean.impl.ConjunctionPredicate;
import com.genia.toolbox.basics.exception.BundledException;
import com.genia.toolbox.basics.exception.technical.TechnicalIOException;
import com.genia.toolbox.constants.client.Charset;
import com.genia.toolbox.spring.provider.plugin.SpringProviderPlugin;
import com.genia.toolbox.uml_generator.Constants;
import com.genia.toolbox.uml_generator.bean.ModelGeneratorContext;
import com.genia.toolbox.uml_generator.manager.ModelGenerator;
import com.genia.toolbox.uml_generator.model.AbstractClassModel;
import com.genia.toolbox.uml_generator.model.AttributeModel;
import com.genia.toolbox.uml_generator.model.EnumerationModel;

/**
 * implementation of {@link ModelGenerator} that creates all Spring related
 * elements.
 */
public class SpringGeneratorImpl
    extends AbstractManager
    implements ModelGenerator
{

  /**
   * the generator.
   * 
   * @param context
   *          the context of the generation
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.uml_generator.manager.ModelGenerator#generate(com.genia.toolbox.uml_generator.bean.ModelGeneratorContext)
   */
  public void generate(ModelGeneratorContext context)
      throws BundledException
  {
    generatePluginClass(context);
    generateSpringConfigFile(context);
  }



  /**
   * generate the spring plugin class and register it to the java services.
   * 
   * @param context
   *          the context of the generation
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private void generatePluginClass(ModelGeneratorContext context)
      throws TechnicalIOException
  {
    try {

      File pluginClassFile = new File(context.getDirectoryForPackage(context.getConfiguration().getGeneratedSrcDir(), context.getConfiguration().getConfigurationPackage()),
          Constants.PLUGIN_CLASS_NAME + ".java");
      pluginClassFile.getParentFile().mkdirs();

      BufferedWriter out = new BufferedWriter(new FileWriter(pluginClassFile));

      out.append("package " + context.getConfiguration().getConfigurationPackage() + ";\n");
      getPrinterManager().printComment(out, "The Plugin that allow to automatically declares Spring beans.");
      out.append("public class ").append(Constants.PLUGIN_CLASS_NAME).append(" extends ").append(Constants.PLUGIN_CLASS_MODEL.getQualifiedName());
      out.append("{\n");
      getPrinterManager().printComment(out, "the main config file.");
      out.append("private static final String MAIN_CONFIG_FILE_RESOURCE = \"/").append(context.getConfiguration().getConfigurationPackage().replace('.', '/')).append("/").append(
          Constants.SPRING_CONFIG_FILE).append("\";\n");
      getPrinterManager().printComment(out, "Constructor.");
      out.append("public ").append(Constants.PLUGIN_CLASS_NAME).append("()\n{\nsuper(MAIN_CONFIG_FILE_RESOURCE);\n}\n");
      out.append("}\n");
      out.close();

      context.getGeneratedSources().add(pluginClassFile);

      Set<String> services = new TreeSet<String>();
      File serviceFile = new File(context.getConfiguration().getResourcesDir(), "META-INF/services/" + SpringProviderPlugin.class.getCanonicalName());
      serviceFile.getParentFile().mkdirs();
      if (serviceFile.exists()) {
        BufferedReader in = new BufferedReader(new FileReader(serviceFile));
        for (String currentLine = in.readLine(); currentLine != null; currentLine = in.readLine()) {
          services.add(currentLine.trim());
        }
      }
      services.add(context.getConfiguration().getConfigurationPackage() + "." + Constants.PLUGIN_CLASS_NAME);

      out = new BufferedWriter(new FileWriter(serviceFile));
      for (String service : services) {
        out.append(service);
        out.append("\n");
      }
      out.close();
    }
    catch (IOException e) {
      throw getExceptionManager().convertException(e);
    }
  }



  /**
   * generate the spring config file.
   * 
   * @param context
   *          the context of the generation
   * @throws BundledException
   *           when an error occurred
   */
  @SuppressWarnings("unchecked")
  private void generateSpringConfigFile(ModelGeneratorContext context)
      throws BundledException
  {
    try {

      File springConfigFile = new File(context.getDirectoryForPackage(context.getConfiguration().getGeneratedResourcesDir(), context.getConfiguration().getConfigurationPackage()),
          Constants.SPRING_CONFIG_FILE);
      springConfigFile.getParentFile().mkdirs();

      BufferedWriter out = new BufferedWriter(new FileWriter(springConfigFile));

      out.append("<?xml version=\"1.0\" encoding=\"").append(Charset.UTF8).append("\"?>\n");
      out
          .append("<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\">\n");

      if (!getCollectionManager().filterList(context.getClasses().values(), Constants.ONLY_GENERATABLE_CLASSES).isEmpty()) {
        out.append("<bean  class=\"").append(Constants.PERSISTABLE_CONFIGURATOR_MODEL.getQualifiedName()).append("\">\n");
        out.append("<property name=\"persistentTypes\"><list>\n");

        for (AbstractClassModel classModel : getCollectionManager().filterList(context.getClasses().values(), Constants.ONLY_GENERATABLE_CLASSES)) {
          if (getModelInspector().isPersistable(classModel)) {
            out.append("<value>").append(classModel.getQualifiedName()).append("</value>\n");
          }
        }
        out.append("</list></property></bean>\n");
      }

      List<EnumerationModel> enumerations = getCollectionManager().filterList(context.getEnumerations().values(), Constants.REMOVE_IGNORED_CLASS_PREDICATE);
      if (!enumerations.isEmpty()) {
        out.append("<bean  class=\"").append(Constants.SIMPLE_MESSAGE_PROVIDER_MODEL.getQualifiedName()).append("\">\n");
        out.append("<property name=\"bundledFiles\"><list>\n");
        for (final EnumerationModel model : enumerations) {
          out.append("<value>").append(getModelDecorator().getMessagesObject(model).getQualifiedName()).append("</value>\n");
        }
        out.write("</list></property></bean>\n");
      }

      if (!context.getDependencies().isEmpty()) {
        for (AbstractClassModel classModel : context.getDependencies()) {
          out.append("<bean class=\"").append(context.getConfiguration().getConfigurationPackage()).append(".").append(Constants.DEPENDENCY_PACKAGE_SUFFIX).append(".").append(classModel.getName())
              .append("\"><property name=\"order\" value=\"100000\"/><property name=\"persistenceManager\" ref=\"persistenceManager\"/>");
          if (!getCollectionManager().filterSet(classModel.getAttributes(), new ConjunctionPredicate<AttributeModel>(Constants.KEEP_TO_ESCAPE_PREDICATE, Constants.REMOVE_UN_MAPPABLE_PREDICATE))
              .isEmpty()) {
            out.append("<property name=\"stringManager\" ref=\"stringManager\"/>");
          }
          out.append("</bean>\n");
        }
      }
      out.write("</beans>\n");

      out.close();
    }
    catch (IOException e) {
      throw getExceptionManager().convertException(e);
    }
  }
}
